﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Diagnostics;

namespace SimpleIR
{
	/// <summary>
	/// Table to lookup body definitions based on invokeids, types etc.
	/// </summary>
	public sealed class BodyDefTable
	{
		//////////////////////////////////////////////
		//Intern the body defs
		private readonly Dictionary<IRInvokeIdentity, IRBodyDef> m_bodydefmap;
		private readonly Dictionary<IRType, List<IRBodyDef>> m_defsigmap;
		private readonly List<IRBodyDef> m_allbodies;

		public BodyDefTable()
		{
			this.m_bodydefmap = new Dictionary<IRInvokeIdentity, IRBodyDef>();
			this.m_defsigmap = new Dictionary<IRType, List<IRBodyDef>>();
			this.m_allbodies = new List<IRBodyDef>();
		}

		/// <summary>
		/// Intern a new body definition.
		/// </summary>
		public void InternNewBodyDef(IRBodyDef bd)
		{
			this.m_bodydefmap.Add(bd.InvokeID, bd);
			if(!this.m_defsigmap.ContainsKey(bd.InvokeID.DefInClass))
				this.m_defsigmap.Add(bd.InvokeID.DefInClass, new List<IRBodyDef>());
			this.m_defsigmap[bd.InvokeID.DefInClass].Add(bd);
			this.m_allbodies.Add(bd);
		}

		/// <summary>
		/// Must be invoke identity (cannot make one from the call either, must have exact sig match).
		/// </summary>
		public IRBodyDef GetBodyForInvokeExactSigMatch(IRInvokeIdentity ivid)
		{ return this.m_bodydefmap[ivid]; }

		/// <summary>
		/// For the interactive display, find the method with the name in the given class, return null if no such method is found.
		/// <c>typeStrs</c> should also include the reciver type (if this is a member method) as the first entry.
		/// If <c>typeStrs</c> is null then we just return the first matching method ignoring the type part of the sig (less typing on command line).
		/// </summary>
		public IRBodyDef GetBodyWithNameInClassOrNull(bool isStatic, string cname, string mname, List<string> typeStrs)
		{
			foreach(IRBodyDef b in m_allbodies)
			{
				bool smatch = (b.InvokeID.InvSig is IRStaticInvoke) == isStatic;
				bool cmatch = b.InvokeID.DefInClass.TypeName.Equals(cname);
				bool mmatch = b.InvokeID.InvSig.InvokeName.Equals(mname);

				if(smatch && cmatch && mmatch)
				{
					bool tsigok = true;
					if(typeStrs != null)
					{
						for(int i = 0; i < b.InvokeID.InvSig.InvokeArgs.Count; ++i)
						{
							String sigstr = b.InvokeID.InvSig.InvokeArgs[i].TypeName;
							tsigok &= typeStrs[i].Equals(sigstr);
						}
					}

					if(tsigok)
						return b;
				}
			}

			return null;
		}

		/// <summary>
		/// Return the list of all bodies in the program.
		/// DO NOT MODIFY LIST CONTENTS
		/// </summary>
		public List<IRBodyDef> BodyList
		{ get { return m_allbodies; } }

		/// <summary>
		/// Return the list of all bodies in the class.
		/// DO NOT MODIFY LIST CONTENTS
		/// </summary>
		public List<IRBodyDef> BodyListForClass(IRType tt)
		{ return m_defsigmap[tt]; }

		/// <summary>
		/// Dump all the definitions to a single string.
		/// </summary>
		public string DumpBodyDefInfo()
		{
			StringBuilder sb = new StringBuilder();
			foreach(IRBodyDef bd in m_allbodies)
			{
				if(bd.IsBuiltin)
					continue;

				sb.Append(bd.ToString() + "\n\n");
			}

			return sb.ToString();
		}
	}
}
